#pragma comment( lib, "d3d9.lib" )
#ifdef _DEBUG
	#pragma comment( lib, "d3dx9d.lib" ) // Matrix
#else
	#pragma comment( lib, "d3dx9.lib" ) // Matrix
#endif
//#pragma comment( lib, "dxerr.lib" )
//#pragma comment( lib, "dxguid.lib" )

#pragma comment( lib, "winmm.lib" )
#pragma comment( lib, "comctl32.lib" )
#pragma comment( lib, "user32.lib" )

#include "../include/dexgel/windowimpldx.hpp"
#include "../include/dexgel/rendererdx.hpp"

#include <d3d9.h>
#include <windows.h>
#include <windowsx.h> // GET_X_LPARAM, GET_Y_LPARAM




namespace dexgel{
namespace priv
{
	const char*      WindowImplDX::ourClassNameA       = "DirectXWindow";
	WindowImplDX*	 WindowImplDX::self_instance = NULL;
}

priv::WindowImplDX::WindowImplDX()
{
}

priv::WindowImplDX::~WindowImplDX()
{
	g3D->deinit(); // Free up the CD3DObj

	if (myHandle)
		DestroyWindow(myHandle);

	UnregisterClass(myTitle.c_str(), GetModuleHandle(NULL) ); // Free up WNDCLASSEX
}


priv::WindowImplDX::WindowImplDX( int kWinWid, int kWinHgt, const std::string& Title )
{
	for(int i=0; i<256; i++) 
		keydown[i] = false; 
	mouseLeft = false;

	canRender = false;
	isOpened = true;
	initTimer();

	self_instance = this;
	myTitle = Title;
	const char *kClassName = myTitle.c_str(); // WNDCLASSEX's name

	WNDCLASSEX wndclassex = {0};
	wndclassex.cbSize          = sizeof(WNDCLASSEX); // Always set to size of WNDCLASSEX
	wndclassex.style           = CS_HREDRAW | CS_VREDRAW;
	wndclassex.lpfnWndProc     = &WindowImplDX::GlobalOnEvent;
	wndclassex.hInstance       = GetModuleHandle(NULL); // If this parameter is NULL, GetModuleHandle returns a pseudo handle to the current process.
	wndclassex.lpszClassName   = kClassName;
	wndclassex.hCursor         = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_SHARED);
	RegisterClassEx(&wndclassex); // Register the WNDCLASSEX

	//WNDCLASSA WindowClass;
	//WindowClass.style         = 0;
	//WindowClass.lpfnWndProc   = &WindowImplDX::GlobalOnEvent;
	//WindowClass.cbClsExtra    = 0;
	//WindowClass.cbWndExtra    = 0;
	//WindowClass.hInstance     = GetModuleHandle(NULL);
	//WindowClass.hIcon         = NULL;
	//WindowClass.hCursor       = 0;
	//WindowClass.hbrBackground = 0;
	//WindowClass.lpszMenuName  = NULL;
	//WindowClass.lpszClassName = ourClassNameA;
	//RegisterClassA(&WindowClass);

	RECT rect = { 0, 0, kWinWid, kWinHgt }; // Desired window client rect

	DWORD winStyleEx = WS_EX_CLIENTEDGE; // This is our extended window style
	DWORD winStyle = WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; // Our window style

	// This function modifies "rect" so it contains the exact dimensions we 
	// need to create a window that has a client rect of kWinWid x kWinHgt
	//AdjustWindowRect(&rect, winStyle, false);
	AdjustWindowRectEx(&rect, winStyle, false, winStyleEx);

	// Create the window
	myHandle = CreateWindowEx(
		winStyleEx,
		kClassName,
		myTitle.c_str(),
		winStyle,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		rect.right - rect.left, // Window width
		rect.bottom - rect.top, // Window height
		NULL,
		NULL,
		GetModuleHandle(NULL), //hinstance,
		NULL);

	//myHandle = CreateWindowA(
	//	kClassName, 
	//	myTitle.c_str(), 
	//	winStyle, 
	//	CW_USEDEFAULT, 
	//	CW_USEDEFAULT, 
	//	rect.right - rect.left, 
	//	rect.bottom - rect.top, 
	//	NULL, 
	//	NULL, 
	//	GetModuleHandle(NULL), 
	//	NULL);

	// Init the global CD3DObj
	g3D = new RendererDx();

	if(g3D->init(myHandle) == false)
		throw char("window creation failed"); // There's been an error

	// DOUBLE CHECK and make sure our client rect is what we want it to be
	GetClientRect(myHandle, &rect);
	assert(rect.right == kWinWid && rect.bottom == rect.bottom);

	ShowWindow(myHandle, SW_SHOW); 
	UpdateWindow(myHandle); // Make sure the window gets initially painted
}


void priv::WindowImplDX::UpdateMessages()
{
	// Handle any OS messages 
	MSG msg;
	if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
	{
		canRender = false; // messages are waiting, don't render a frame

		if(msg.message == WM_QUIT)
		{
			isOpened = false;	
			return;
		}

		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}else{
		canRender = true;
	}

}

bool priv::WindowImplDX::IsOpened()
{
	return isOpened;
}

Rendrer* priv::WindowImplDX::getRendrer()
{
	return g3D;
}

void priv::WindowImplDX::ProcessEvent( UINT Message, WPARAM WParam, LPARAM LParam )
{
	// Don't process any message until window is created
	if (myHandle == NULL)
		return;

	switch (Message)
	{
		// Destroy event
	case WM_DESTROY :
		{
			PostQuitMessage(0);
			isOpened = false;
			break;
		}

	//	// Set cursor event
	//case WM_SETCURSOR :
	//	{
	//		// The mouse has moved, if the cursor is in our window we must refresh the cursor
	//		if (LOWORD(LParam) == HTCLIENT)
	//			SetCursor(myCursor);

	//		break;
	//	}

		// Close event
	case WM_CLOSE :
		{
			isOpened = false;
			//Event Evt;
			//Evt.Type = Event::Closed;
			//SendEvent(Evt);
			break;
		}

	//	// Resize event
	//case WM_SIZE :
	//	{
	//		// Update window size
	//		RECT Rect;
	//		GetClientRect(myHandle, &Rect);
	//		myWidth  = Rect.right - Rect.left;
	//		myHeight = Rect.bottom - Rect.top;

	//		Event Evt;
	//		Evt.Type        = Event::Resized;
	//		Evt.Size.Width  = myWidth;
	//		Evt.Size.Height = myHeight;
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Gain focus event
	//case WM_SETFOCUS :
	//	{
	//		Event Evt;
	//		Evt.Type = Event::GainedFocus;
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Lost focus event
	//case WM_KILLFOCUS :
	//	{
	//		Event Evt;
	//		Evt.Type = Event::LostFocus;
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Text event
	//case WM_CHAR :
	//	{
	//		if (myKeyRepeatEnabled || ((LParam & (1 << 30)) == 0))
	//		{
	//			Event Evt;
	//			Evt.Type = Event::TextEntered;
	//			Evt.Text.Unicode = static_cast<Uint32>(WParam);
	//			SendEvent(Evt);
	//		}
	//		break;
	//	}

		// Keydown event
	case WM_KEYDOWN :
	case WM_SYSKEYDOWN :
		{
			//if (myKeyRepeatEnabled || ((HIWORD(LParam) & KF_REPEAT) == 0))
			//{
			//	Event Evt;
			//	Evt.Type        = Event::KeyPressed;
			//	Evt.Key.Alt     = HIWORD(GetAsyncKeyState(VK_MENU))    != 0;
			//	Evt.Key.Control = HIWORD(GetAsyncKeyState(VK_CONTROL)) != 0;
			//	Evt.Key.Shift   = HIWORD(GetAsyncKeyState(VK_SHIFT))   != 0;
			//	Evt.Key.Code    = VirtualKeyCodeToSF(WParam, LParam);
			//	SendEvent(Evt);
			//}
			keydown[WParam] = true;
			break;
		}

		// Keyup event
	case WM_KEYUP :
	case WM_SYSKEYUP :
		{
			//Event Evt;
			//Evt.Type        = Event::KeyReleased;
			//Evt.Key.Alt     = HIWORD(GetAsyncKeyState(VK_MENU))    != 0;
			//Evt.Key.Control = HIWORD(GetAsyncKeyState(VK_CONTROL)) != 0;
			//Evt.Key.Shift   = HIWORD(GetAsyncKeyState(VK_SHIFT))   != 0;
			//Evt.Key.Code    = VirtualKeyCodeToSF(WParam, LParam);
			//SendEvent(Evt);
			keydown[WParam] = false;
			break;
		}

	//	// Mouse wheel event
	//case WM_MOUSEWHEEL :
	//	{
	//		Event Evt;
	//		Evt.Type = Event::MouseWheelMoved;
	//		Evt.MouseWheel.Delta = static_cast<Int16>(HIWORD(WParam)) / 120;
	//		SendEvent(Evt);
	//		break;
	//	}

		// Mouse left button down event
	case WM_LBUTTONDOWN :
		{
			mouseLeft = true;
			//Event Evt;
			//Evt.Type               = Event::MouseButtonPressed;
			//Evt.MouseButton.Button = Mouse::Left;
			//Evt.MouseButton.X      = LOWORD(LParam);
			//Evt.MouseButton.Y      = HIWORD(LParam);
			//SendEvent(Evt);
			mousx = LOWORD(LParam);
			mousy = HIWORD(LParam);

			break;
		}

		// Mouse left button up event
	case WM_LBUTTONUP :
		{
			mouseLeft = false;
			//Event Evt;
			//Evt.Type               = Event::MouseButtonReleased;
			//Evt.MouseButton.Button = Mouse::Left;
			//Evt.MouseButton.X      = LOWORD(LParam);
			//Evt.MouseButton.Y      = HIWORD(LParam);
			//SendEvent(Evt);
			mousx = LOWORD(LParam);
			mousy = HIWORD(LParam);

			break;
		}

	//	// Mouse right button down event
	//case WM_RBUTTONDOWN :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonPressed;
	//		Evt.MouseButton.Button = Mouse::Right;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Mouse right button up event
	//case WM_RBUTTONUP :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonReleased;
	//		Evt.MouseButton.Button = Mouse::Right;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Mouse wheel button down event
	//case WM_MBUTTONDOWN :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonPressed;
	//		Evt.MouseButton.Button = Mouse::Middle;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Mouse wheel button up event
	//case WM_MBUTTONUP :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonReleased;
	//		Evt.MouseButton.Button = Mouse::Middle;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Mouse X button down event
	//case WM_XBUTTONDOWN :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonPressed;
	//		Evt.MouseButton.Button = HIWORD(WParam) == XBUTTON1 ? Mouse::XButton1 : Mouse::XButton2;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

	//	// Mouse X button up event
	//case WM_XBUTTONUP :
	//	{
	//		Event Evt;
	//		Evt.Type               = Event::MouseButtonReleased;
	//		Evt.MouseButton.Button = HIWORD(WParam) == XBUTTON1 ? Mouse::XButton1 : Mouse::XButton2;
	//		Evt.MouseButton.X      = LOWORD(LParam);
	//		Evt.MouseButton.Y      = HIWORD(LParam);
	//		SendEvent(Evt);
	//		break;
	//	}

		// Mouse move event
	case WM_MOUSEMOVE :
		{
			//// Check if we need to generate a MouseEntered event
			//if (!myIsCursorIn)
			//{
			//	TRACKMOUSEEVENT MouseEvent;
			//	MouseEvent.cbSize    = sizeof(TRACKMOUSEEVENT);
			//	MouseEvent.hwndTrack = myHandle;
			//	MouseEvent.dwFlags   = TME_LEAVE;
			//	TrackMouseEvent(&MouseEvent);

			//	myIsCursorIn = true;

			//	Event Evt;
			//	Evt.Type = Event::MouseEntered;
			//	SendEvent(Evt);
			//}

			//Event Evt;
			//Evt.Type        = Event::MouseMoved;
			//Evt.MouseMove.X = LOWORD(LParam);
			//Evt.MouseMove.Y = HIWORD(LParam);
			//SendEvent(Evt);
			//break;
			mousx = LOWORD(LParam);
			mousy = HIWORD(LParam);
			break;
		}

	//	// Mouse leave event
	//case WM_MOUSELEAVE :
	//	{
	//		myIsCursorIn = false;

	//		Event Evt;
	//		Evt.Type = Event::MouseLeft;
	//		SendEvent(Evt);
	//		break;
	//	}
	}
}

LRESULT CALLBACK priv::WindowImplDX::GlobalOnEvent( HWND Handle, UINT Message, WPARAM WParam, LPARAM LParam )
{
	// Associate handle and Window instance when the creation message is received
	if (Message == WM_CREATE)
	{
		// Get WindowImplWin32 instance (it was passed as the last argument of CreateWindow)
		long This = reinterpret_cast<long>(reinterpret_cast<CREATESTRUCT*>(LParam)->lpCreateParams);

		// Set as the "user data" parameter of the window
		SetWindowLongPtr(Handle, GWLP_USERDATA, This);
	}

	// Get the WindowImpl instance corresponding to the window handle
	//WindowImplDX* Window = reinterpret_cast<WindowImplDX*>(GetWindowLongPtr(Handle, GWLP_USERDATA));
	WindowImplDX* Window = self_instance;
	

	// Forward the event to the appropriate function
	if (Window)
	{
		Window->ProcessEvent(Message, WParam, LParam);

		//if (Window->myCallback)
		//	return CallWindowProc(reinterpret_cast<WNDPROC>(Window->myCallback), Handle, Message, WParam, LParam);
	}

	// We don't forward the WM_CLOSE message to prevent the OS from automatically destroying the window
	if (Message == WM_CLOSE)
		return 0;

	return DefWindowProcA(Handle, Message, WParam, LParam);
}


void priv::WindowImplDX::initTimer()
{
	__int64 freq;

	// Check if we have a performance counter
	if (QueryPerformanceFrequency((LARGE_INTEGER*) &freq))
	{
		// Performance counter is available => use it!
		hasPerformanceCounter = true;

		// Counter resolution is 1 / counter frequency
		timerResolution = 1.0 / (double) freq;

		// Set start time for timer
		QueryPerformanceCounter((LARGE_INTEGER*) &t0_64);
	}
	else
	{
		// No performace counter available => use the tick counter
		hasPerformanceCounter = false;

		// Counter resolution is 1 ms
		timerResolution = 0.001;

		// Set start time for timer
		t0_32 = (int)getTime();
	}
}


double priv::WindowImplDX::getTime()
{
	double t;
	__int64 t_64;

	if (hasPerformanceCounter)
	{
		QueryPerformanceCounter((LARGE_INTEGER*) &t_64);
		t =  (double)(t_64 - t0_64);
	}
	else
		t = (double)(getTime() - t0_32);

	// Calculate the current time in seconds
	return t * timerResolution;
}




} // eof dexgel

#if defined(_WIN32)

#include <windows.h>

extern int main(int argc, char* argv[]);

int WINAPI WinMain(HINSTANCE in_hinstance, HINSTANCE in_hprev, PSTR in_cmdline, int in_ishow)
{
	//hinstance = in_hinstance;
	//hprev = in_hprev;
	//cmdline = in_cmdline;
	//ishow = in_ishow;
	return main(__argc, __argv);
}

#endif // _WIN32